package com.cfcoding.network;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.LinkedList;

/**
 *
 * @author Tristan
 */
public class NetworkAdapter {

    private NetworkInterface ni;
    private static IPValidator validator = new IPValidator();

    /**
     * Gets a list of the available NetworkAdapters
     * @return LinkedList
     */
    public static LinkedList<NetworkAdapter> getAdapters() {
        LinkedList<NetworkAdapter> adapters = new LinkedList<NetworkAdapter>();
        try {
            Enumeration nets = NetworkInterface.getNetworkInterfaces();
            while (nets.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) nets.nextElement();
                NetworkAdapter na = new NetworkAdapter(ni);
                if (na.hasIPAddress()) {
                    adapters.add(na);
                }
            }
        } catch (Exception e) {
        } finally {
            return adapters;
        }
    }

    public static NetworkAdapter getAdapter(String id) {
        StringBuilder sb = new StringBuilder(18);
        for (NetworkAdapter n : getAdapters()) {
            if (n.getMACAddress().equals(id)) {
                return n;
            }
        }
        return null;
    }

    /**
     * Creates a NetworkAdapter instance of the NetworkInterface
     * @param ni
     */
    public NetworkAdapter(NetworkInterface ni) {
        this.ni = ni;
    }

    /**
     * Gets the IPv4 address if it exists. If not, checks for IPv6 address.
     * @return IPv4 or IPv6 address, or null if no address was found
     */
    public String getIP() {
        if (getIPv4() != null) {
            return getIPv4();
        } else if (getIPv6() != null) {
            return getIPv6();
        }
        return null;
    }

    /**
     * Gets the IPv4 address of the adapter
     * @return the IPv4 address
     * @throws SocketException
     */
    public String getIPv4() {
        try {
            InetAddress ia;
            for (int i = 0; i < getAddress().size(); i++) {
                ia = (InetAddress) getAddress().get(i);
                if (validator.isIPv4(ia.getHostAddress())) {
                    return ia.getHostAddress();
                }
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Gets the IPv6 address of the adapter
     * @return the IPv6 address
     * @throws SocketException
     */
    public String getIPv6() {
        try {
            InetAddress ia;
            for (int i = 0; i < getAddress().size(); i++) {
                ia = (InetAddress) getAddress().get(i);
                if (validator.isIPv6(ia.getHostAddress())) {
                    return ia.getHostAddress();
                }
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Checks for an IPv4 or IPv6 address
     * @return true if an IP address exists, false otherwise
     */
    public boolean hasIPAddress() {
        return getIPv4() != null || getIPv6() != null;
    }

    /**
     * Gets the default gateway address of the adapter
     * @return the gateway address
     */
    public String getDefaultGateway() {
        try {
            Process result = Runtime.getRuntime().exec("ipconfig");

            BufferedReader br = new BufferedReader(new InputStreamReader(result.getInputStream()));
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains("Address") && (getIPv4() == null ? false : getIPv4().equals(validator.matchIPv4(line)))) {
                    while ((line = br.readLine()) != null) {
                        if (line.contains("Default Gateway")) {
                            return validator.matchIPv4(line);
                        }
                    }
                }
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    public String getMACAddress() {
        try {
            StringBuilder sb = new StringBuilder(18);
            for (byte b : this.getInterface().getHardwareAddress()) {
                if (sb.length() > 0) {
                    sb.append(':');
                }
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (SocketException ex) {
            return "";
        }
    }

    /**
     * Gets the NetworkInterface of this NetworkAdapter 
     * @return the NetworkInterface
     */
    public NetworkInterface getInterface() {
        return ni;
    }

    /**
     * Gets a list of addresses, excluding the loopback
     * @return list of InetAddresses
     * @throws SocketException
     */
    private LinkedList getAddress() throws SocketException {
        LinkedList<InetAddress> list = new LinkedList<InetAddress>();
        Enumeration inets = ni.getInetAddresses();
        while (inets.hasMoreElements()) {
            InetAddress i = (InetAddress) inets.nextElement();
            if (!ni.isLoopback()) {
                list.add(i);
            }
        }
        return list;
    }

    @Override
    public String toString() {
        return ni.getDisplayName();
    }
}
